package com.yc.mybatis.core;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;

/**
 * 
 * @author 27594
 *
 */
public class SqlSessionFactory {

	private String config = "mybatis-config.xml";
	private List<String> mappers = new ArrayList<String>();
	//存放数据库连接
	private Map<String, String> dataSourceMap = new HashMap<String, String>();
	private ConnectionPool connectionPool;
	//存放解析映射文件的信息
	private Map<String, MapperInfo> mapperInfos = new HashMap<String, MapperInfo>();

	public SqlSessionFactory(String config) {
		this.config = config;
		init();
	}

	private void init() {
		readConfigXml(); //读取解析配置文件

		readMapperXml(); //读取解析映射文件

		//创建数据库连接池
		connectionPool = ConnectionPoolUtil.getInstance();

	}

	/**
	 * 读取解析配置文件
	 */
	@SuppressWarnings("unchecked")
	private void readConfigXml() {
		SAXReader reader = new SAXReader();
		try(InputStream is = this.getClass().getClassLoader().getResourceAsStream(config)){
			Document doc = reader.read(is); //转换成文档树

			//通过xpath来查找路径
			XPath xpath = doc.createXPath("//dataSource/property");

			List<Element> properties = xpath.selectNodes(doc);

			properties.forEach(item -> {
				dataSourceMap.put(item.attributeValue("name"), item.attributeValue("value"));
			});

			/*
			 * dataSourceMap.forEach((key, val) -> { System.out.println(key + " : " + val);
			 * });
			 */

			xpath = doc.createXPath("//mappers/mapper");
			List<Element> listMappers = xpath.selectNodes(doc);
			if(listMappers == null || listMappers.isEmpty()) {
				return;
			}
			listMappers.forEach(item ->{
				mappers.add(item.attributeValue("resource"));
			});

		} catch (IOException e) {
			e.printStackTrace();
		} catch (DocumentException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 读取解析映射文件
	 */
	@SuppressWarnings("unchecked")
	private void readMapperXml() {
		if(mappers == null || mappers.isEmpty()) {
			return;
		}
		SAXReader reader = new SAXReader();
		MapperInfo mapperInfo;
		Document doc = null;
		XPath xpath = null;
		List<Element> nodes = null;
		String basePath = ""; //存放命名空间的值
		String nodeName = null; //节点名
		String sql = null; //sql语句
		String id = null; 

		Pattern pattern = null;
		Matcher matcher = null;
		List<String> params = null;

		for(String mapper: mappers) { //一个文件一个文件的读取解析
			basePath = "";
			try(InputStream is = this.getClass().getClassLoader().getResourceAsStream(mapper)){
				doc = reader.read(is); //转换成文档树

				basePath = doc.getRootElement().attributeValue("namespace"); //获取根元素的属性值
				
				if(basePath != null || !"".equals(basePath)) {
					basePath += ".";
				}
				
				//通过xpath来查找路径
				xpath = doc.createXPath("//mapper/*");
				nodes = xpath.selectNodes(doc); //从文档树中查找满足条件的点

				if(nodes == null || nodes.isEmpty()) { //说明没有任何语句，则进行下一次循环
					continue;
				}
				for(Element node : nodes) {
					mapperInfo = new MapperInfo();
					
					nodeName = node.getName().toLowerCase(); //获取当前节点的名称
					if("select".equals(nodeName)) {
						mapperInfo.setUpdate(false); //说明是查询
					}
					
					mapperInfo.setParameterType(node.attributeValue("parameterType"));
					mapperInfo.setResultType(node.attributeValue("resultType"));
					
					sql = node.getTextTrim(); //获取节点的文本值
					System.out.println(sql);
					
					pattern = Pattern.compile("[#][{]\\w+}");  //#{xxx}
					matcher = pattern.matcher(sql); //根据规则在sql语句中匹配
					
					params = new ArrayList<String>();
					while(matcher.find()) { //从头开始查找
						params.add(matcher.group().replaceAll("[#{}]*", ""));
					}
					mapperInfo.setParams(params);
					
					sql = matcher.replaceAll("?");
					mapperInfo.setSql(sql);
					id = basePath + node.attributeValue("id");
					mapperInfos.put(id, mapperInfo);
				}
				
				mapperInfos.forEach((key, val)->{
					System.out.println(key + " : " + val);
				});
				
			} catch (IOException e) {
				e.printStackTrace();
			} catch (DocumentException e) {
				e.printStackTrace();
			}
		}
	}
	
	public SqlSession openSession() {
		return new SqlSession(connectionPool, mapperInfos);
	}
}
